package com.tools.commons.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.slf4j.Logger;

import java.io.IOException;
import java.text.SimpleDateFormat;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * @author wangminggen
 */
public class JsonUtil {
    private static final Logger log = getLogger(JsonUtil.class);

    private JsonUtil() {
    }

    private static final ObjectMapper MAPPER = new ObjectMapper();
    private static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    private static final SimpleDateFormat FMT = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);

    static {
        MAPPER.setDateFormat(FMT);
        //在反序列化时忽略在 JSON 中存在但 Java 对象不存在的属性
        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        //在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSS
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //在序列化时忽略值为 null 的属性
        MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    /**
     * 取出json中某filed
     *
     * @param json
     * @param filed
     * @return
     */
    public static String get(String json, String filed) {
        JsonNode readTree = null;
        try {
            readTree = MAPPER.readTree(json);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return readTree.isNull() ? null : readTree.get(filed).toString();
    }

    public static JsonNode parseJsonNode(String json) {
        JsonNode readTree = null;
        try {
            readTree = MAPPER.readTree(json);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return readTree;
    }

    private static <T> String obj2String(T obj, boolean format) {
        if (obj == null || obj instanceof String) {
            return (String) obj;
        }
        try {
            return format ? MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj) : MAPPER.writeValueAsString(obj);
        } catch (Exception e) {
            log.warn("Parse Object to String error", e);
            return null;
        }
    }

    public static <T> String obj2String(T obj) {
        return obj2String(obj, false);
    }

    public static <T> String obj2StringPretty(T obj) {
        return obj2String(obj, true);
    }

    /**
     * 解析普通对象
     *
     * @param str
     * @param clazz
     * @param <T>
     * @return
     */
    @SuppressWarnings(value = "unchecked")
    public static <T> T string2Obj(String str, Class<T> clazz) {
        if (StringUtils.isEmpty(str) || clazz == null) {
            return null;
        }
        try {
            return clazz.equals(String.class) ? (T) str : MAPPER.readValue(str, clazz);
        } catch (Exception e) {
            log.warn("Parse String to Object error", e);
            return null;
        }
    }

    /**
     * 解析多层集合嵌套
     *
     * @param str
     * @param typeReference
     * @param <T>
     * @return
     */
    @SuppressWarnings(value = "unchecked")
    public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
        if (StringUtils.isEmpty(str) || typeReference == null) {
            return null;
        }
        try {
            return (T) (typeReference.getType().equals(String.class) ? str : MAPPER.readValue(str, typeReference));
        } catch (Exception e) {
            log.warn("Parse String to Object error", e);
            return null;
        }
    }

    /**
     * 解析一层集合嵌套
     *
     * @param str
     * @param collectionClass
     * @param elementClasses
     * @param <T>
     * @return
     */
    public static <T> T string2Obj(String str, Class<?> collectionClass, Class<?>... elementClasses) {
        if (StringUtils.isEmpty(str)) {
            return null;
        }
        JavaType javaType = MAPPER.getTypeFactory().constructParametricType(collectionClass, elementClasses);
        try {
            return MAPPER.readValue(str, javaType);
        } catch (Exception e) {
            log.warn("Parse String to Object error", e);
            return null;
        }
    }
}

